package cn.net.dpi.shp.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.sevenzipjbinding.*;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.Tika;

import java.io.File;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class UnCompressUtil {
    public static final String SHP_FILE_SUFFIX = "shp";
    private static final boolean ENABLE_DEBUG_LOG = false;
    private static final List<String> COMRESSED_FILE_TYPE_LIST = Arrays.asList("application/zip", "x-rar-compressed");

    public static class ExtractCallback implements IArchiveExtractCallback {
        private int hash = 0;
        private int size = 0;
        private int index;
        private boolean skipExtraction;
        private IInArchive inArchive;
        private String saveDirectory;

        public ExtractCallback(IInArchive inArchive, String saveDirectory) {
            this.inArchive = inArchive;
            this.saveDirectory = saveDirectory;
        }

        @Override
        public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException {
            this.index = index;
            skipExtraction = (Boolean) inArchive.getProperty(index, PropID.IS_FOLDER);
            if (skipExtraction || extractAskMode != ExtractAskMode.EXTRACT) {
                return null;
            }
            String filePath = saveDirectory + File.separator + inArchive.getProperty(index, PropID.PATH);
            File targetFile = new File(filePath);
            return data -> {
                FileUtil.writeBytes(data, targetFile, 0, data.length, true);
                if (ENABLE_DEBUG_LOG) {
                    System.out.println("index = " + index + ", extractAskMode = " +
                            extractAskMode + ",filePath = " + filePath + ",data.length=" + data.length + ",size=" + size);
                }
                hash ^= Arrays.hashCode(data);
                size += data.length;
                return data.length; // Return amount of proceed data
            };
        }

        @Override
        public void prepareOperation(ExtractAskMode extractAskMode)
                throws SevenZipException {
        }

        @Override
        public void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException {
            if (skipExtraction) {
                return;
            }
            if (extractOperationResult != ExtractOperationResult.OK) {
                log.error("Extraction error");
            } else {
                if (ENABLE_DEBUG_LOG) {
                    System.out.println(String.format("%9X | %10s | %s", hash, size, inArchive.getProperty(index, PropID.PATH)));
                }
                hash = 0;
                size = 0;
            }
        }

        @Override
        public void setCompleted(long completeValue) throws SevenZipException {
        }

        @Override
        public void setTotal(long total) throws SevenZipException {
        }
    }

    private static String getUnCompressedDirectory(String compressedFilePath) {
        if (StringUtils.isEmpty(compressedFilePath)) {
            throw new RuntimeException("compressedFilePath empty.");
        }
        String unCompressedDirectory = StringUtils.substringBeforeLast(compressedFilePath, ".");
        if (!FileUtil.exist(unCompressedDirectory)) {
            FileUtil.mkdir(unCompressedDirectory);
        }
        return unCompressedDirectory;
    }

    private static void checkCompressedFileType(String compressedFilePath) {
        Tika tika = new Tika();
        String detect = tika.detect(compressedFilePath);
        log.info("unCompressFile:{}, type:{}", compressedFilePath, detect);
    }


    private static String unCompressByZip(String compressedFilePath) {
        checkCompressedFileType(compressedFilePath);
        File unzip = ZipUtil.unzip(compressedFilePath);
        return unzip.getAbsolutePath();
    }

    /**
     * 只支持zip
     *
     * @param compressedFilePath
     * @return
     */
    public static String unCompressFile(String compressedFilePath) {
        return unCompressByZip(compressedFilePath);
    }

    private static String unCompressByServenZip(String compressedFilePath) {
        checkCompressedFileType(compressedFilePath);
        try (
                RandomAccessFile randomAccessFile = new RandomAccessFile(compressedFilePath, "r");
                // autodetect archive type
                IInArchive inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
        ) {
            if (ENABLE_DEBUG_LOG) {
                System.out.println("   Hash   |    Size    | Filename");
                System.out.println("----------+------------+---------");
            }
            int[] in = new int[inArchive.getNumberOfItems()];
            for (int i = 0; i < in.length; i++) {
                in[i] = i;
            }

            String unCompressedDirectory = getUnCompressedDirectory(compressedFilePath);
            // Non-test mode
            inArchive.extract(in, false, new ExtractCallback(inArchive, unCompressedDirectory));
            return getUnCompressedDirectory(compressedFilePath);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Error occurs:{}", e.getMessage());
            return "";
        }
    }

    public static List<String> getFiles(String directory, String suffixFilter) {
        File[] files = FileUtil.ls(directory);
        return Arrays.stream(files)
                .map(File::getAbsolutePath)
                .filter(f -> StringUtils.endsWith(f, suffixFilter)|| "hd_lane_group.dbf".equals(FileUtil.getName(f)))
                .collect(Collectors.toList());
    }

    private static void testUnCompressRar5() {
        String file = "D:\\data\\2024\\12\\DEMO-shp_4.78MB241023-zip.zip";
        String unCompressedDirectory = unCompressByServenZip(file);
        List<String> files = getFiles(unCompressedDirectory, SHP_FILE_SUFFIX);
        files.forEach(System.out::println);
        if (FileUtil.del(unCompressedDirectory)) {
            System.err.println("delete directory:" + unCompressedDirectory);
        }
    }

    private static void testUnCompressZip() {
        String file = "D:\\data\\2024\\12\\DEMO-shp_4.78MB241023-zip.zip";
        String path = unCompressFile(file);
        String unCompressedDirectory = getUnCompressedDirectory(file);
        List<String> files = getFiles(unCompressedDirectory, SHP_FILE_SUFFIX);
        files.forEach(System.out::println);
        if (FileUtil.del(path)) {
            System.err.println("delete directory:" + path);
        }
    }

    public static void main(String[] args) {
        testUnCompressZip();
    }
}
